红队视角下的AWS横向移动
0x01 前言
本文主要从红队视角讲述AWS公有云中的一些横向移动思路以及对应的一些身份和权限基础。
概括一下AWS横向移动思路:在获得一个凭据(AK、SK等)的前提下,枚举所有可访问资源,再通过可控制资源获得更多的凭据,获得更多资源控制权。
所以本文从下面三个方面介绍AWS横向移动:
1. AWS中的身份和访问管理机制
2. AWS中资源以及资源特殊性
3. AWS一些常见横向移动思路
0x02 AWS身份与访问管理
AWS身份与访问管理机制也就是IAM,官方对IAM的介绍是:“AWS Identity and Access Management (IAM) 是一种 Web 服务,可以帮助您安全地控制对 AWS 资源的访问。可以使用 IAM 来控制谁通过了身份验证(准许登录)并获得授权(拥有权限)来使用资源。”
简单来说AWS基于IAM机制实现了谁可以对什么资源进行什么操作的控制。
2.1 AWS中的用户与身份
用户分为根用户和普通用户:
根用户是在创建AWS账户时就建立的一个用户,具有该账户下的最高权限。根用户可以再根据需求创建一些有各种各样权限的普通用户,这些普通用户可以在各种不同的用途中发挥作用。类似企业在AWS中进行注册后获得一个管理员权限,这个管理员再为运维,开发人员等创建不同权限的普通用户。用户可以通过账号密码登录AWS控制台,也可以通过AK、SK实现编程访问。
除了用户,AWS中的身份还包括用户组和角色等:
为了用户统一管理,可以给用户分组,如开发组和运维组,然后再加上不同的权限;而为了方便权限的统一管理,可以创建不同的角色,分配不同的权限,如运维组长。
2.2 IAM权限配置
IAM中权限配置不同于其它ACL机制,是以策略(policies)来描述权限配置,这种形式下的权限配置更灵活。
下面是一个策略配置的demo: 配置了该策略的用户允许在2022/7/1之前对任何资源做IAM相关的任何操作
IAM策略中存在以下几个要素:
• 资源:EC2,Bucket,ECS等。
• 权限:执行操作的权限。
• 上下文:时间,位置,网络,资源等。
• 身份:附加到用户/用户组/角色/EC2实例上。
由于策略具体配置比较复杂,这里就不展开了。
下面介绍一些策略的分类,主要分为基于身份的策略和基于资源的策略:
identity based:附加到用户等身份上,说明了用户可以对哪些资源进行哪些操作,又细分为下面两种策略:
• 托管策略:独立的策略,可以建立好之后长期使用的。
AWS内置了很大适用于各种场景下的托管策略,当然也可以自己创建策略:
• 内联策略:直接附加到用户上的策略,单独为某个用户加权限。
• sources based:直接在资源(如储存桶)上配置某些用户可以访问,如储存桶可公开访问这种配置。
为了方便管理,可以把多个权限组合为一个角色:
0x03 AWS资源以及其特殊性
前面提到横向移动的过程也是不断获取资源,然后再根据资源获取更多凭据的过程,下面解释这么说的理由。
以EC2为例,EC2很符合我们对资源的认知,属于一种计算资源。稍微了解过公有云安全的应该都知道metadata,可以获取EC2实例的metadata凭据然后再访问其它资源,EC2中的metadata到底是什么呢?
根据AWS官方文档[1]可以知道,为了方便运行在EC2上的程序访问AWS中的其它资源,可以配置instance profile来给EC2实例绑定一个角色,这样运行在实例上的所有程序都有绑定角色配置的权限。metadata请求的也就是EC2绑定角色权限的一个凭据。
这就是AWS中资源的特殊性,是资源的同时,也是一个AWS中的一个虚拟用户。
除了EC2,AWS中其它服务lambda/api-getway/ECS等均可以绑定角色。
0x04 横向移动思路
铺垫了这么多,下面介绍一些AWS横向移动的思路。
4.1 枚举资源和权限
获得一个凭据要做的第一件事情就是要枚举当前用户所拥有的权限和资源,可以使用PACU[2]或者ScoutSuite[3]等工具辅助完成。
4.2 EC2 Metadata
创建EC2实例的时候可以为EC2创建一个profile,profile可以看作是实例的说明书,查看配置文档[4]可以知道,其中的可配置项其实很少,只有 InstanceProfileName、Path和 Roles 可以从metadata服务中获取绑定role的临时凭据:
$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2Master
不过官方为了增强Metadata安全性,推出了IMDSv2,V2版本服务请求元数据要先PUT获取Token再带token请求,在一定程度上缓解了公有云环境下SSRF的危害:
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/
4.3 EC2 Userdata
在启动ec2实例时,可以设置一些userdata用于执行常见自动配置任务。userdata大概有两类:shell脚本和cloud-init指令。cloud-init指令类似shell脚本:
用户数据会在实例启动时自动执行,AWS设置它的本意是在EC2启动之前执行一些自动化的任务。用户使用不当可能造成凭据的泄露,如泄露数据库账号密码:
另外攻击者可以修改userdata并控制实例重启来在实例内执行恶意命令或者实现持久化。
# 写恶意代码
aws ec2 run-instances --image-id ami-abcd1234 --count 1 --instance-type m3.medium
--key-name my-key-pair --subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 --user-data file://my_script.txt
# 重启
aws ec2 stop-instances --instance-ids i-xxxx
aws ec2 start-instances --instance-ids i-xxxx
4.4 ECS 横向移动
ECS集群是一个弹性的容器服务,包括EC2实例和单个容器实例,用户可以在集群运行多个任务,一个集群大概情况如下:
集群上主要有有EC2和FARGATE两种计算资源,对应任务的两种启动类型:Fargate启动类型和ec2启动类型。不过不管是什么类型,任务都是以容器为单位划分资源的,也就是说任务是跑在docker容器中的。
Fargate 启动类型:只需定义任务,无需预置和管理后台基础设施,ECS会自动创建容器运行该任务。
EC2 启动类型:在集群中管理的 EC2 实例上创建容器并运行任务。
服务与任务的关系是: 服务是任务的分组,可以运行多个任务。
在获取到EC2实例权限时,可以列举运行在EC2上的容器:
因为一个Task会绑定一个执行的Role,EC2也会绑定一个Role,所以就有下面三种横向思路:
横向思路1 EC2->Tasks
在控制EC2实例的时候,在容器内执行命令,获取任务绑定Role的凭据:
; docker exec <container-id> sh -c 'wget -O- 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'
横向思路2 Tasks->EC2
在获取到单个容器时,如果容器启动类型为EC2类型,可以尝试获取宿主EC2中的凭据,也就是meta data:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role
横向思路3 Task迁移
集群内可能有多个EC2,满足以下条件可把别的EC2中运行的任务迁移到被控EC2实例:
1. 获取到一个EC2实例控制权。
2. 有update-container-instances-state权限,可以关闭或者暂停运行其它集群内的EC2实例。
关闭其它EC2实例后,由于ECS会自动调度任务,所以会把任务迁移到被控EC2,这样又可以利用横向思路1获取任务Role的凭据。
暂停其它EC2:
运行在另外一个EC2的任务容器会迁移到被控EC2上:
4.5 AssumeRole
AssumeRole操作权限:从aws文档[5]中该权限描述可以看出是一个可以更改角色权限的操作,简单来说就是可以获取某个Role权限的临时凭据,类似Windows域中的委派。
AssumeRole权限配置方式有两种:
1. 在target tole处设置:如下面这个配置,设置AssumeRolePolicyDocument 的 Principal,power-role这个role对assumerole_role_target有assumeRole权限。
# assumerole_role_target
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws-cn:iam::123456789012:role/power-role"
}
}
]
}
1. 在powerful role处设置:
另外这个信任还是可以跨账号的:
• 同一账号下,role B的信任关系中有role A的ARN就够了。
• 跨账号的场景下,role C(另一个账号的role,信任关系中要包含role A的ARN,role A也要有assume role的权限才能玩)。
如下面发现当前用户对cg-lambda-invoker*有AssumeRole权限:
执行assumeRole返回对应role的临时凭据: aws sts assume-role --role-arn [cg-lambda-invoker_arn] --role-session-name [whatever_you_want_here]
4.6 PassRole
通过aws文档[6]对passrole的介绍可以了解passRole的作用,概括来说就是可以为某个服务赋予某个IAM Role,之后这个服务就有对应Role的权限。但是特殊的是,这个"服务"也可也是个用户,也就是说在有passRole权限的情况下,可以为某个用户增添某个Role。
awscli下具体操作是attach policy,如下图为赋予chris用户administrator权限:
4.7 lambda提权
Lambda创建的时候同样需要绑定一个Role,在实战时可以枚举Lambda查看有无可执行敏感操作的函数:
# 枚举lambda函数
aws lambda list-functions
# 查看lambda函数内容
aws lambda get-function –function-name <lambda-name>
# 调用
aws lambda invoke --function-name <lambda-name> --cli-binary-format raw-in-base64-out --payload <params> out.txt
如发现某lambda函数可以为用户添加策略:
调用lambda函数直接为当前用户添加Administrator权限。
4.8 更改托管策略版本提权
基于身份的策略中,托管策略是分版本的:
存在如下两个条件时,可以更改策略版本提权:
1. 历史版本中有较高权限
2. 当前版本策略有setDefaultPolicyVersion权限
如发现当前用户有setDefaultPolicyVersion权限:
并且发现托管策略历史版本V5,有较高权限:
更改版本提权:aws iam set-default-policy-version –policy-arn <policy-arn> –version-id v5 –profile rollbackuser。
4.9 RDS突破访问限制
RDS是AWS的云数据库产品,在创建数据库时可以选择是否可公开访问(公网访问)。
突破RDS数据库设置为不允许公开访问的限制有两个思路:
1. 拿到VPC中的机器后,通过机器直接访问数据库。
2. 为不公开访问数据库创建快照,再根据快照创建一共可公开访问的数据库实例。
是否可公开访问可以通过PubliclyAccessible字段判断:
1. 创建快照:
aws rds create-db-snapshot –db-instance-identifier <DBInstanceIdentifier> –db-snapshot test-shot
查看快照信息可以用aws rds describe-db-snapshots。
1. 创建新的数据库实例,指定 --publicly-accessible。
aws rds restore-db-instance-from-db-snapshot --db-instance-identifier <new-DBInstanceIdentifier> --db-snapshot-identifier test-shot --db-subnet-group-name <sub-net-group-name> --publicly-accessible --vpc-security-group-ids <sg-group-id>
同时要有一个开放了数据库端口的安全组, 枚举安全组和subnet可以用下面的命令:
aws ec2 describe-security-groups
aws rds describe-db-subnet-groups
我这里使用默认的安全组,端口是全开的。
aws rds restore-db-instance-from-db-snapshot --db-instance-identifier database-2 --db-snapshot-identifier test-shot --publicly-accessible --profile super
1. 重置新数据库密码
aws rds modify-db-instance –db-instance-identifier database-2 –master-user-password P@ssw0rd123 –profile super
等待数据库密码重置完毕,就可以连接。
4.10 敏感信息收集
在一些服务中需要用户传递一些参数,一般来说有两种方式,环境变量参数和secretManager。
如:云函数环境中的变量参数存在AK/SK。
AWS官方建议使用secret Manager传递敏感参数,所以也可以尝试枚举secretManager中的参数。
参考:
https://github.com/RhinoSecurityLabs/cloudgoat
https://aws.amazon.com/cn/
https://dhiyaneshgeek.github.io/cloud/security/2022/06/23/aws-misconfigurations/
https://ruse.tech/blogs/ecs-attack-methods
引用链接:
[1] AWS官方文档:
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
[2] PACU:
https://github.com/RhinoSecurityLabs/pacu/
[3] ScoutSuite:
https://github.com/nccgroup/ScoutSuite
[4] 配置文档:
https://docs.aws.amazon.com/zh_cn/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html
[5] aws文档:
https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html
[6] aws文档:
https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_roles_use_passrole.html